package com.tgh.gles2.c7.cubemap;

import com.tgh.gles2.BaseRenderer;
import com.tgh.gles2.R;
import com.tgh.utils.GLBatch;
import com.tgh.utils.GLFrame;
import com.tgh.utils.GLFrustum;
import com.tgh.utils.GLGeometryTransform;
import com.tgh.utils.GLMatrixStack;
import com.tgh.utils.GLShaderManager;
import com.tgh.utils.GLTools;
import com.tgh.utils.GLTriangleBatch;
import com.tgh.utils.Matrix44f;
import android.content.Context;
import android.opengl.GLES20;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class CubemapRenderer extends BaseRenderer{
    
    
    public CubemapRenderer(Context context) {
        super(context);
        projectionMatrix = new GLMatrixStack();
        modelViewMatrix = new GLMatrixStack();
        transformPipleLine = new GLGeometryTransform();
        cameraFrame=new GLFrame();
        cubeBatch = GLTools.makeCube(20);
        sphereBatch = new GLTriangleBatch();
        if (!sphereBatch.loadDataFromSD("sphere_1_52_26")) {
            sphereBatch=GLTools.makeSphere(1f, 52, 26);
            sphereBatch.cacheDataToSD("sphere_1_52_26");
        }
    }
    
    public void up() {
        cameraFrame.moveForward(0.1f);
    }
    public void down() {
        cameraFrame.moveForward(-0.1f);
    }
    public void left() {
        cameraFrame.rotateLocalY(0.5f);
    }
    public void right() {
        cameraFrame.rotateLocalY(-0.5f);
    }
    
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        createTextures();
        initSkyBoxProgram();
        initRelectionProgram();
        GLES20.glEnable(GLES20.GL_DEPTH_TEST);        
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
        viewFrustum = new GLFrustum(35, (float)width/(float)height, 1, 1000);
        projectionMatrix.loadMatrix(viewFrustum.getProjectionMatrix());
        transformPipleLine.setMatrixStack(modelViewMatrix, projectionMatrix);
        cameraFrame.moveForward(-4f);        
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT|GLES20.GL_DEPTH_BUFFER_BIT);
        
        Matrix44f cameraRotOnly = cameraFrame.getCameraMatrix(true);
        Matrix44f camera = cameraFrame.getCameraMatrix(false);
        Matrix44f inverseCamera=new Matrix44f();
        inverseCamera.load(cameraRotOnly);
        inverseCamera.inverse();
        modelViewMatrix.pushMatrix();
            modelViewMatrix.multMatrix(camera);
            GLES20.glUseProgram(reflectionShader);
            GLES20.glUniformMatrix4fv(locReflPMatrix, 1, false, projectionMatrix.getMatrix().getArray(), 0);
            GLES20.glUniformMatrix4fv(locReflMvMatrix, 1, false, modelViewMatrix.getMatrix().getArray(), 0);
            GLES20.glUniformMatrix4fv(locReflInverseCamera, 1, false, inverseCamera.getArray(), 0);
            GLES20.glEnable(GLES20.GL_CULL_FACE);
            sphereBatch.draw();
            GLES20.glDisable(GLES20.GL_CULL_FACE);
        modelViewMatrix.popMatrix();
        
        modelViewMatrix.pushMatrix();
            modelViewMatrix.multMatrix(cameraRotOnly);
            GLES20.glUseProgram(skyBoxShader);
            GLES20.glUniformMatrix4fv(locSkyMvpMatrix, 1, false, transformPipleLine.getModelViewProjectionMatrix().getArray(), 0);
            cubeBatch.draw();
        modelViewMatrix.popMatrix();        
    }
    
    @Override
    public void onSurfaceDestroyed() {
    }
    
    protected void initSkyBoxProgram(){
        String vpSrc=GLShaderManager.readFileFromAssets(mContext, "SkyBox.vp");
        String fpSrc=GLShaderManager.readFileFromAssets(mContext, "SkyBox.fp");
        skyBoxShader = GLShaderManager.createProgram(vpSrc, fpSrc, new int[]{GLShaderManager.GL_ATTRIBUTE_VERTEX}, new String[]{"vVertex"});
        locSkyMvpMatrix = GLES20.glGetUniformLocation(skyBoxShader, "mvpMatrix");
    }
    
    protected void initRelectionProgram(){
        String vpSrc=GLShaderManager.readFileFromAssets(mContext, "Reflection.vp");
        String fpSrc=GLShaderManager.readFileFromAssets(mContext, "Reflection.fp");
        reflectionShader = GLShaderManager.createProgram(vpSrc, fpSrc,
                new int[]{GLShaderManager.GL_ATTRIBUTE_VERTEX,GLShaderManager.GL_ATTRIBUTE_NORMAL},
                new String[]{"vVertex","vNormal"} );
        locReflMvMatrix = GLES20.glGetUniformLocation(reflectionShader, "mvMatrix");
        locReflPMatrix = GLES20.glGetUniformLocation(reflectionShader, "pMatrix");
        locReflInverseCamera = GLES20.glGetUniformLocation(reflectionShader, "inverseCameraMatrix");
    }
    
    protected void createTextures() {
        int[] textures = GLTools.genTextures(1);
        cubeTextures=textures[0];
        GLES20.glBindTexture(GLES20.GL_TEXTURE_CUBE_MAP, cubeTextures);
        GLTools.loadDrawableToTextureCube(
                mContext, 
                resIds, 
                cubes, 
                GLES20.GL_LINEAR_MIPMAP_LINEAR, 
                GLES20.GL_LINEAR, 
                GLES20.GL_CLAMP_TO_EDGE, 
                true);
    }
    
    private int[] cubes={
            GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_X,
            GLES20.GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
            GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
            GLES20.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
            GLES20.GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
            GLES20.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
    };
    
    private int[] resIds={
            R.drawable.posx,
            R.drawable.negx,
            R.drawable.posy,
            R.drawable.negy,
            R.drawable.posz,
            R.drawable.negz
    };
    
    protected int cubeTextures;
    protected int skyBoxShader;
    protected int reflectionShader;
    protected GLBatch cubeBatch;
    protected GLFrame cameraFrame;
    protected GLFrustum viewFrustum;
    protected GLMatrixStack modelViewMatrix;
    protected GLMatrixStack projectionMatrix;
    protected GLGeometryTransform transformPipleLine;
    protected int locSkyMvpMatrix;
    protected int locReflMvMatrix;
    protected int locReflPMatrix;
    protected int locReflInverseCamera;
    protected GLTriangleBatch sphereBatch;
}
